package com.alibaba.nacos.client.naming.beat;

import com.alibaba.nacos.client.monitor.MetricsMonitor;
import com.alibaba.nacos.client.naming.net.NamingProxy;
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
import com.alibaba.nacos.client.utils.LogUtils;
import com.example.demo.retry.SpringContextHolder;
//import org.springframework.boot.actuate.metrics.MetricsEndpoint;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

public class BeatReactor {
    private ScheduledExecutorService executorService;
    private NamingProxy serverProxy;
    public final Map<String, BeatInfo> dom2Beat;

    public BeatReactor(NamingProxy serverProxy) {
        this(serverProxy, UtilAndComs.DEFAULT_CLIENT_BEAT_THREAD_COUNT);
    }

    public BeatReactor(NamingProxy serverProxy, int threadCount) {
        this.dom2Beat = new ConcurrentHashMap();
        this.serverProxy = serverProxy;
        this.executorService = new ScheduledThreadPoolExecutor(threadCount, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setDaemon(true);
                thread.setName("com.alibaba.nacos.naming.beat.sender");
                return thread;
            }
        });
    }

    public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
        LogUtils.NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo);
        String key = this.buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort());
        BeatInfo existBeat = null;
        if ((existBeat = (BeatInfo)this.dom2Beat.remove(key)) != null) {
            existBeat.setStopped(true);
        }

        this.dom2Beat.put(key, beatInfo);
        this.executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS);
        MetricsMonitor.getDom2BeatSizeMonitor().set((double)this.dom2Beat.size());
    }

    public void removeBeatInfo(String serviceName, String ip, int port) {
        LogUtils.NAMING_LOGGER.info("[BEAT] removing beat: {}:{}:{} from beat map.", new Object[]{serviceName, ip, port});
        BeatInfo beatInfo = (BeatInfo)this.dom2Beat.remove(this.buildKey(serviceName, ip, port));
        if (beatInfo != null) {
            beatInfo.setStopped(true);
            MetricsMonitor.getDom2BeatSizeMonitor().set((double)this.dom2Beat.size());
        }
    }

    private String buildKey(String serviceName, String ip, int port) {
        return serviceName + "#" + ip + "#" + port;
    }

    class BeatTask implements Runnable {
        BeatInfo beatInfo;

        public BeatTask(BeatInfo beatInfo) {
            this.beatInfo = beatInfo;
        }

        @Override
        public void run() {
            if (!this.beatInfo.isStopped()) {
                long result = BeatReactor.this.serverProxy.sendBeat(this.beatInfo);
                this.beatInfo.setWeight(getWeight());
                BeatReactor.this.serverProxy.setWeight(this.beatInfo);
                long nextTime = result > 0L ? result : this.beatInfo.getPeriod();
                BeatReactor.this.executorService.schedule(BeatReactor.this.new BeatTask(this.beatInfo), nextTime, TimeUnit.MILLISECONDS);
            }
        }
    }

    private List<String> HEALTH_NAMES = Arrays.asList("system.cpu.usage","process.cpu.usage");
//    private MetricsEndpoint bean = new MetricsEndpoint(new SimpleMeterRegistry());
    private float getWeight() {
        try {
//            SpringBeanAutowiringSupport
//            MetricsEndpoint bean = SpringContextHolder.getBean(MetricsEndpoint.class);
//            double cpu = 0.0d;
//            for (String e:HEALTH_NAMES) {
//                MetricsEndpoint.MetricResponse metric = bean.metric(e, null);
//                if(metric==null){
//                    return 1.0F;
//                }
//                double value = metric.getMeasurements().get(0).getValue();
//                cpu += value;
//            }
//            int freeCpu = (int) ((1-cpu)*100) - 20;
            int freeCpu = (int) ((1-0)*100) - 20;
            if(freeCpu<=0){
                return 0.0F;
            }
            return freeCpu;
        } catch (Exception e){
            return 1.0F;
        }

    }
}